package scheduler

import (
	"context"
	"errors"
	"strconv"
	"sync"

	"gitee.com/wujianqiang/golibs/pkg/logging"
	"github.com/nats-io/nats.go"
)

// 定义事件
type EventType string

const (
	Event_Task_Start            EventType = "task_start"
	Event_Task_Submit           EventType = "task_submit"
	Event_Task_Del              EventType = "task_del"
	Event_Task_End              EventType = "task_end"
	Event_Task_Error            EventType = "task_error"
	Event_Task_Cancel           EventType = "task_cancel"
	Event_Task_Dataset_Download EventType = "task_dataset_download"
	Event_Task_Status_Change    EventType = "task_status_change"
	Event_Task_Every_Run        EventType = "task_every_run"
)

type Event interface {
	EventName() string
	Action(data interface{}) interface{}
	Cancel(data interface{}) interface{}
}

// 控制着event 执行，SchedulerEnd 返回true，说明该event完成，否则，下个调度周期还是会调用。
type SchedulerPolicy interface {
	EventName() string
	SchedulerEnd(data interface{}) (bool, error)
}

type PipeLine struct {
	ID           string
	Name         string
	Events       []string
	Args         map[string]interface{}
	Current      int
	Length       int
	SchedulerRun bool
	sync.Mutex
	Context      context.Context
	Cancel       context.CancelFunc
	EventsResult map[string]interface{}
}
type DelTask struct {
	CancelTask CancelTask
	Msg        *nats.Msg
}

func (p *PipeLine) GetCurrentEvent() (string, map[string]interface{}) {
	return p.Events[p.Current], p.Args
}
func (p *PipeLine) NextEvent() error {
	if p.Current >= p.Length-1 {
		return errors.New("end the pipeline")
	}
	p.Current++
	return nil
}
func (p *PipeLine) PreEvent() error {
	if p.Current <= 0 {
		return errors.New("start the pipeline")
	}
	p.Current--
	return nil
}
func (p *PipeLine) SetCurrentEvent(eventName string) error {
	for index, event := range p.Events {
		if event == eventName {
			p.Current = index
			return nil
		}
	}
	return errors.New("can't find event:" + eventName)
}
func (p *PipeLine) SetSchedulerRun(status bool) {
	p.Mutex.Lock()
	defer p.Mutex.Unlock()
	p.SchedulerRun = status
}
func (p *PipeLine) GetSchedulerRun() bool {
	p.Mutex.Lock()
	defer p.Mutex.Unlock()
	return p.SchedulerRun
}
func (p *PipeLine) GetIndexEvent(eventName string) (int, error) {
	for index, event := range p.Events {
		if event == eventName {
			return index, nil

		}
	}
	return -1, errors.New("can't find event:" + eventName)
}

var WebHookWhenBefore = "before"
var WebHookWhenAfter = "after"
var WebHookWhenBoth = "both"

type WebHookEventData struct {
	When   string      `json:"when"`
	Event  string      `json:"event"`
	Action string      `json:"action"`
	Data   interface{} `json:"data"`
	Code   string      `json:"code"`
}
type WebHook interface {
	Exec(data WebHookEventData) error
	CheckWhen(when string, data interface{}) bool
}

// type WebHook struct {
// 	Url           string
// 	HttpMethod    string
// 	AUthorization string
// 	When          string
// }

// func (wh *WebHook) Exec(data interface{}) error {
// 	client := &http.Client{}
// 	reader, er := json.Marshal(data)
// 	if er != nil {
// 		return er
// 	}
// 	sdata := bytes.NewReader(reader)
// 	req, err := http.NewRequest(wh.HttpMethod, wh.Url, sdata)
// 	if err != nil {
// 		return err
// 	}
// 	if len(wh.AUthorization) > 0 {
// 		req.Header.Set("Authorization", wh.AUthorization)
// 	}
// 	req.Header.Set("Content-Type", "application/json")
// 	resp, err := client.Do(req)
// 	if err != nil {
// 		return err
// 	}
// 	defer resp.Body.Close()
// 	if resp.StatusCode != 200 {
// 		return errors.New("http status code:" + strconv.Itoa(resp.StatusCode))
// 	}
// 	return nil
// }

type Action string

// client 端产生的任务
type SubTask struct {
	Events     []string               `json:"events" mapstructure:"events"`
	JobId      string                 `json:"jobid" mapstructure:"jobid"`
	EventIndex string                 `json:"eventid" mapstructure:"eventid"`
	Name       string                 `json:"name" mapstructure:"name"`
	Payload    map[string]interface{} `json:"payload" mapstructure:"payload"`
}

func (s SubTask) ToPipeline() *PipeLine {
	ctx, cancel := context.WithCancel(context.Background())
	index, er := strconv.Atoi(s.EventIndex)
	if er != nil {
		logging.Logger.Error(er)
		index = 0
	}
	p := PipeLine{
		Events:       s.Events,
		ID:           s.JobId,
		Args:         s.Payload,
		Current:      index,
		Length:       len(s.Events),
		Name:         s.Name,
		Context:      ctx,
		Cancel:       cancel,
		EventsResult: make(map[string]interface{}),
	}
	return &p
}

// 更新任务数据，如果 startevent,更新后，从startevent开始重新启动.
// 意味着，如果pipeline 执行到startevent之后的event，必须cancal到startevent
type UpdateTask struct {
	JobId      string                 `json:"jobid"`
	StartEvent string                 `json:"startevent"`
	Payload    map[string]interface{} `json:"payload"`
}

// 取消事件,pipeline中events，从当前执行的event执行cancel 到endEvent
type CancelTask struct {
	JobId    string                 `json:"jobid"`
	EndEvent string                 `json:"endevent"`
	Payload  map[string]interface{} `json:"payload"`
}

var ClientSub = "sub"
var ClientUpdate = "update"
var ClientDel = "del"

//action:sub/cancel
type WokerEvent struct {
	Event   string                 `json:"event"`
	Action  string                 `json:"action"`
	Payload map[string]interface{} `json:"payload"`
}
